import os
import binascii
import re
import os
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
from werkzeug.security import generate_password_hash, check_password_hash

class EncryptionUtils:
    # 密码复杂度正则（至少8位，包含大小写字母、数字和特殊字符）
    PASSWORD_PATTERN = re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@#$%^&+=!]).{8,}$')
    
    @staticmethod
    def hash_password(password):
        """生成密码哈希"""
        return generate_password_hash(password)
    
    @staticmethod
    def verify_password(password, hash_value):
        """验证密码哈希"""
        return check_password_hash(hash_value, password)
    
    @staticmethod
    def validate_password(password):
        """验证密码复杂度"""
        if not EncryptionUtils.PASSWORD_PATTERN.match(password):
            return False, "Password must be at least 8 characters, contain uppercase, lowercase, number and special character"
        return True, "Valid password"
    
    @staticmethod
    def binary_to_hex(binary_data):
        """将二进制数据转换为十六进制字符串"""
        return binascii.hexlify(binary_data).decode('utf-8')

    @staticmethod
    def generate_rsa_key_pair():
        """生成RSA密钥对"""
        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )
        public_key = private_key.public_key()
        
        private_pem = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption()
        )
        
        public_pem = public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo
        )
        
        return private_pem, public_pem

    @staticmethod
    def load_public_key(public_key_pem):
        if isinstance(public_key_pem, str):
            public_key_pem = public_key_pem.encode('utf-8')
        return serialization.load_pem_public_key(public_key_pem, backend=default_backend())
    
    @staticmethod
    def rsa_encrypt(public_key_pem, data):
        """RSA公钥加密"""
        if isinstance(public_key_pem, str):
            public_key_pem = public_key_pem.encode('utf-8')
        public_key = serialization.load_pem_public_key(public_key_pem, backend=default_backend())
        return public_key.encrypt(
            data,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA256()),
                algorithm=hashes.SHA256(),
                label=None
            )
        )
    
    @staticmethod
    def rsa_decrypt(private_key_pem, encrypted_data):
        """RSA私钥解密"""
        if isinstance(private_key_pem, str):
            private_key_pem = private_key_pem.encode('utf-8')
        private_key = serialization.load_pem_private_key(private_key_pem, password=None, backend=default_backend())
        return private_key.decrypt(
            encrypted_data,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA256()),
                algorithm=hashes.SHA256(),
                label=None
            )
        )
    
    @staticmethod
    def aes_encrypt(key, plaintext):
        """AES-GCM加密"""
        nonce = os.urandom(12)
        cipher = Cipher(algorithms.AES(key), modes.GCM(nonce))
        encryptor = cipher.encryptor()
        ciphertext = encryptor.update(plaintext) + encryptor.finalize()
        return ciphertext, nonce, encryptor.tag
    
    @staticmethod
    def aes_decrypt(key, ciphertext, nonce, tag):
        """AES-GCM解密"""
        if isinstance(tag, str):
            tag = binascii.unhexlify(tag)
        if isinstance(nonce, str):
            nonce = binascii.unhexlify(nonce)
        # 验证nonce长度是否符合要求（8-128字节）
        if len(nonce) < 8 or len(nonce) > 128:
            raise ValueError(f"Nonce length must be between 8 and 128 bytes, got {len(nonce)} bytes")
        cipher = Cipher(algorithms.AES(key), modes.GCM(nonce, tag))
        decryptor = cipher.decryptor()
        return decryptor.update(ciphertext) + decryptor.finalize()
    

    @staticmethod
    def generate_key():
        """生成对称密钥（256位AES密钥）"""
        return os.urandom(32)
    
    @staticmethod
    def generate_symmetric_key():
        """生成对称密钥（256位AES密钥）"""
        return os.urandom(32)
        
    @staticmethod
    def generate_nonce():
        """生成GCM nonce（12字节）"""
        return os.urandom(12)

    @staticmethod
    def derive_kek(password, salt):
        """推导KEK密钥"""
        if isinstance(salt, str):
            salt = binascii.unhexlify(salt)
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=480000,
            backend=default_backend()
        )
        return kdf.derive(password.encode('utf-8'))

    @staticmethod
    def derive_kek_from_key(key, salt):
        """从密钥派生KEK密钥"""
        if isinstance(salt, str):
            salt = binascii.unhexlify(salt)
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=480000,
            backend=default_backend()
        )
        return kdf.derive(key)
    
    @staticmethod
    def binary_to_hex(data):
        """二进制转十六进制字符串"""
        return binascii.hexlify(data).decode('utf-8')
    
    @staticmethod
    def hex_to_binary(hex_str):
        """十六进制字符串转二进制，处理奇数长度的输入和非十六进制字符"""
        try:
            # 移除非十六进制字符
            hex_str = ''.join(c for c in hex_str if c in '0123456789abcdefABCDEF')
            if not hex_str:
                raise ValueError("Empty hex string after removing non-hex characters")
            # 处理奇数长度的十六进制字符串
            if len(hex_str) % 2 != 0:
                hex_str = '0' + hex_str
            return binascii.unhexlify(hex_str)
        except Exception as e:
            from app import logger
            logger.error(f"Error converting hex to binary: {e}, input: {hex_str}")
            raise
    
    @staticmethod
    def file_encrypt(input_file_path, output_file_path, key):
        """加密文件并保存到输出路径"""
        nonce = os.urandom(12)
        cipher = Cipher(algorithms.AES(key), modes.GCM(nonce), backend=default_backend())
        encryptor = cipher.encryptor()

        with open(input_file_path, 'rb') as f_in, open(output_file_path, 'wb') as f_out:
            # 写入nonce
            f_out.write(nonce)
            # 写入tag占位符（16字节）
            f_out.write(b' ' * 16)
            # 加密文件内容
            while True:
                chunk = f_in.read(65536)  # 64KB块
                if not chunk:
                    break
                f_out.write(encryptor.update(chunk))
            # 完成加密并写入tag
            f_out.write(encryptor.finalize())
            f_out.seek(12)  # 移动到tag位置
            f_out.write(encryptor.tag)

        return nonce, encryptor.tag

    @staticmethod
    def file_decrypt(input_file_path, output_file_path, key):
        """解密文件并保存到输出路径"""
        with open(input_file_path, 'rb') as f_in, open(output_file_path, 'wb') as f_out:
            # 读取nonce和tag
            nonce = f_in.read(12)
            tag = f_in.read(16)
            # 读取加密内容
            ciphertext = f_in.read()

            # 解密
            cipher = Cipher(algorithms.AES(key), modes.GCM(nonce, tag), backend=default_backend())
            decryptor = cipher.decryptor()
            decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()
            f_out.write(decrypted_data)

        return True

    @staticmethod
    def generate_file_key():
        """生成文件加密专用密钥"""
        return os.urandom(32)  # 256位AES密钥